/*此代码练习来自《javascript高级程序设计》第六章--继承*/
/*1.原型链继承 */
function Parent(){
  this.ability = 'singing';
}
Parent.prototype.showTalent = function(){
   return this.ability;
}
function Child(){
  this.grownup = false;
}

Child.prototype = new Parent();
let child1 = new Child();
console.log(child1.showTalent());// singing
//通过instanceof确定原型和实例之间的关系
console.log(child1 instanceof Child); // true
console.log(child1 instanceof Parent); //true
console.log(child1 instanceof Object);// true

//原型链继承的问题：最主要的问题是来自包含引用类型值的原型(因为包含引用类型值的原型属性会被所有实例共享)
function Toy(){
   this.toys = ['bear','bird','cat'];
}
function Child(){}
Child.prototype = new Toy();//Child的prototype变成了Toy的一个实例

let child1 = new Child(); 
child1.toys.push('rabbit');
console.log(child1.toys);// bear bird cat rabbit

let child2 = new Child();
console.log(child2.toys); // bear bird cat rabbit
//结果所有的实例都会共享此toys属性
//第二个问题是在创建子类型的实例时，不能向超类型Toy的构造函数中传递参数

/*2.借用构造函数继承 */
function Toy(belong){
  this.toys = ['bear','bird','cat'];
  this.belong = belong;
}
function Child(){
  Toy.call(this,'nick');
  this.age = 10;
}
let child1 = new Child();
child1.toys.push('horse');
console.log(child1.toys); // bear bird cat horse

let child2 = new Child();
console.log(child2.toys); // bear bird cat
//构造函数继承问题：方法都在构造函数中定义，不利于函数复用

/*3.组合继承 */
function Parent(childName){
  this.childName = childName;
  this.toys = ['bear','bird','cat'];
}
Parent.prototype.sayName = function(){
  alert(this.childName)
}
function Child(name,age){
  Parent.call(this,name);
  this.age = age;
}

Child.prototype = new Parent();
let child1 = new Child('nick',10);
child1.toys.push('horse');
console.log(child1.toys);// bear bird cat horse
child1.sayName();//nick

let child2 = new Child('jack',8);
console.log(child2.toys);// bear bird cat
child2.sayName();//jack

//组合继承避免了原型链和借用构造函数的缺陷，融合二者有点，是js中最常用的继承模式

/*4.原型式继承 */
function object(o){
  function F();
  F.prototype = o;
  return new F();
}

let person = {
  name: 'jack',
  friends: ['sheldon','matt','taylor']
}
let anotherPerson = object(person);
anotherPerson.friends.push('brad');

alert(person.friends); // sheldon,matt,taylor,brad
//适用于：没必要创建构造函数，但想让一个对象与另一个对象保持类似。（注意引用类型值会共享）上面的object和es方法Object.create()效果相同

/*5.寄生式继承 */
function createAnother(o){
  let clone = Object.create(o);
  clone.sayHi = function(){
    alert('hi');
  }
  return clone;
}
let person = {
  name: 'jack'
}

let anotherP = createAnother(person);
anotherP.sayHi(); //'hi'
//任何返回新对象的函数都适用于此模式，缺点是不能做到函数复用而降低效率，这点和构造函数类似

/*6.寄生组合式继承 */
//组合继承虽最常用，也存在不足：无论什么情况下，都会调用两次超类型构造函数（上面例子中的Parent），如下：

function Parent(childName){
  this.childName = childName;
  this.toys = ['bear','bird','cat'];
}
Parent.prototype.sayName = function(){
  alert(this.childName)
}
function Child(name,age){
  Parent.call(this,name); //第二次调用Parent()，实例中会有childName和toys属性
  this.age = age;
}

Child.prototype = new Parent(); // 第一次调用Parent(),原型中会有childName和toys属性
let child1 = new Child('nick',10);
//解决：寄生组合式继承：是引用类型最理想的继承模式
function createObj(o){
  function F(){}
  F.prototype = o;
  return new F;
}
function inherit(child,parent){
  let prototype = createObj(parent.prototype);
  prototype.constructor = child;
  child.prototype = prototype;
}//关键的一步
function Parent(childName){
  this.childName = childName;
  this.toys = ['bear','bird','cat'];
}
Parent.prototype.sayName = function(){
  alert(this.childName)
}
function Child(name,age){
  Parent.call(this,name); 
  this.age = age;
}

inherit(Child,Parent);